using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using Cassandraemon.Util;
using Apache.Cassandra;


namespace Cassandraemon
{


	public class CassandraEntity<T>
	{
		public CassandraContext Context { get; set; }
		
		public CassandraBinary Key { get; set; }
		
		public string ColumnFamily { get; set; }
		
		public CassandraBinary SuperColumn { get; set; }
		
		public CassandraBinary Column { get; set; }
		
		public Dictionary<object, CassandraBinary> Index { get; set; }
		
		public T Data { get; set; }
		
		
		
		public CassandraEntity() 
		{
			if(!typeof(T).Equals(typeof(Column)) &&
			   !typeof(T).Equals(typeof(SuperColumn)) &&
			   !typeof(T).Equals(typeof(CounterColumn)) &&
			   !typeof(T).Equals(typeof(CounterSuperColumn)) &&
			   !typeof(T).Equals(typeof(List<Column>)) &&
			   !typeof(T).Equals(typeof(List<SuperColumn>)) &&
			   !typeof(T).Equals(typeof(List<CounterColumn>)) &&
			   !typeof(T).Equals(typeof(List<CounterSuperColumn>)))
			{
				throw new InvalidOperationException("CassandraEntity's generic parameter must be specified Column, SuperColumn, CounterColumn, SuperCounterColumn, ColumnList, SuperColumnList, CounterColumnList or SuperCounterColumnList.");
			}
		}
		                                     
		
		
		
		
		public CassandraEntity<T> SetKey(object key)
		{
			Key = key.ToCassandraByte();
			return this;
		}
		
		public CassandraEntity<T> SetColumnFamily(string columnFamily)
		{
			ColumnFamily = columnFamily;
			return this;
		}
		
		public CassandraEntity<T> SetSuperColumn(object superColumn)
		{
			SuperColumn = superColumn.ToCassandraByte();
			
			if(typeof(T).Equals(typeof(SuperColumn)))
			{
				if(Data == null) Data = (T)(object)new SuperColumn();
				(Data as SuperColumn).Name = SuperColumn;
			}
			else if(typeof(T).Equals(typeof(CounterSuperColumn)))
			{
				if(Data == null) Data = (T)(object)new CounterSuperColumn();
				(Data as CounterSuperColumn).Name = SuperColumn;
			}
			
			return this;
		}
		
		public CassandraEntity<T> SetColumn(object column)
		{
			Column = column.ToCassandraByte();
			
			if(typeof(T).Equals(typeof(Column)))
			{
				if(Data == null) Data = (T)(object)new Column();
				(Data as Column).Name = Column;
			}
			else if(typeof(T).Equals(typeof(CounterColumn)))
			{
				if(Data == null) Data = (T)(object)new CounterColumn();
				(Data as CounterColumn).Name = Column;
			}
			
			return this;
		}
		
		public CassandraEntity<T> SetData(T data)
		{
			Data = data;
			return this;
		}
	
		public CassandraEntity<T> SetData(object obj)
		{
			return SetData(obj, null);
		}
		
		public CassandraEntity<T> SetData(object obj, object columnname)
		{
			Type type = typeof(T);
			
			if(type.Equals(typeof(Column)))
			{
				Column = columnname == null ? null : columnname.ToCassandraByte();
				
				Column column = new Column();
				column.Name = Column;
				column.Value = obj.ToCassandraByte();
				Data = (T)(object)column;
			}
			else if(type.Equals(typeof(SuperColumn)))
			{
				SuperColumn = columnname == null ? null : columnname.ToCassandraByte();
				
				SuperColumn superColumn = new SuperColumn();
				superColumn.Name = SuperColumn;
				superColumn.Columns = MappingUtil.MappingColumn(obj);
				Data = (T)(object)superColumn;
			}
			else if(type.Equals(typeof(CounterColumn)))
			{
				Column = columnname == null ? null : columnname.ToCassandraByte();
				
				CounterColumn column = new CounterColumn();
				column.Name = Column;
				column.Value = (long)obj;
				Data = (T)(object)column;
			}
			else if(type.Equals(typeof(List<Column>)))
			{
				Data = (T)(object)MappingUtil.MappingColumn(obj);
			}
			else if(type.Equals(typeof(List<SuperColumn>)))
			{
				if(Data == null) Data = (T)(object)new List<SuperColumn>();
				
				if(obj.GetType().GetInterface("IEnumerable") != null)
				{
					IEnumerable list = obj as IEnumerable;
					foreach(object o in list)
					{
						SuperColumn superColumn = new SuperColumn();
						superColumn.Columns = MappingUtil.MappingColumn(o);
						(Data as List<SuperColumn>).Add(superColumn);
					}
				}
				else
				{
					SuperColumn = columnname == null ? null : columnname.ToCassandraByte();
				
					SuperColumn superColumn = new SuperColumn();
					superColumn.Name = SuperColumn;
					superColumn.Columns = MappingUtil.MappingColumn(obj);
					(Data as List<SuperColumn>).Add(superColumn);
				}
			}
			else
			{
				throw new InvalidCastException("SetData(obj) method accept Column or SuperColumn or CounterColumn or ColumnList or SuperColumnList to CassandraEntity's generic type.");
			}
			
			return this;
		}
		
		public CassandraEntity<T> SetDataAndColumnFamily(object obj)
		{
			return SetDataAndColumnFamily(obj, null);
		}
		
		public CassandraEntity<T> SetDataAndColumnFamily(object obj, object columnname)
		{
			ColumnFamily = obj.GetType().Name;
			return SetData(obj, columnname);
		}
		

		private ObjectCassandraEntityConverter<T, TOutput> GetObjectConverter<TOutput>()
		{
			return new ObjectCassandraEntityConverter<T, TOutput>(this);
		}
		
		private ListCassandraEntityConverter<T, List<TOutputItem>, TOutputItem> GetListConverter<TOutputItem>()
		{
			return new ListCassandraEntityConverter<T, List<TOutputItem>, TOutputItem>(this);
		}
		
		private DictionaryCassandraEntityConverter<T, Dictionary<TOutputKey, TOutputValue>, TOutputKey, TOutputValue> GetDictionaryConverter<TOutputKey, TOutputValue>()
		{
			return new DictionaryCassandraEntityConverter<T, Dictionary<TOutputKey, TOutputValue>, TOutputKey, TOutputValue>(this);
		}
		
		
		public TOutput ToObject<TOutput>()
		{
			return GetObjectConverter<TOutput>().ToObject();
		}
		
		public List<TOutputItem> ToObjectList<TOutputItem>()
		{
			return GetListConverter<TOutputItem>().ToObjectList();
		}
		
		public List<TOutputItem> ToFlatNameList<TOutputItem>()
		{
			return GetListConverter<TOutputItem>().ToFlatNameList();
		}
		
		public List<TOutputItem> ToFlatValueList<TOutputItem>()
		{
			return GetListConverter<TOutputItem>().ToFlatValueList();
		}
		
		public Dictionary<TOutputKey, TOutputValue> ToObjectDictionary<TOutputKey, TOutputValue>()
		{
			return GetDictionaryConverter<TOutputKey, TOutputValue>().ToObjectDictionary();
		}
		
		public Dictionary<TOutputKey, TOutputValue> ToFlatDictionary<TOutputKey, TOutputValue>()
		{
			return GetDictionaryConverter<TOutputKey, TOutputValue>().ToFlatDictionary();
		}
		
		public Dictionary<TOutputKey, TOutputValue> ToFlatValueKeyDictionary<TOutputKey, TOutputValue>()
		{
			return GetDictionaryConverter<TOutputKey, TOutputValue>().ToFlatValueKeyDictionary();
		}
		
		public Dictionary<TOutputKey, List<TListItem>> ToNameListDictionary<TOutputKey, TListItem>()
		{
			return GetDictionaryConverter<TOutputKey, List<TListItem>>().ToNameListDictionary<TListItem>();
		}
		
		public Dictionary<TOutputKey, List<TListItem>> ToValueListDictionary<TOutputKey, TListItem>()
		{
			return GetDictionaryConverter<TOutputKey, List<TListItem>>().ToValueListDictionary<TListItem>();
		}
		
		public Dictionary<TOutputKey, Dictionary<TDictionaryKey, TDictionaryValue>> ToDictionaryDictionary<TOutputKey, TDictionaryKey, TDictionaryValue>()
		{
			return GetDictionaryConverter<TOutputKey, Dictionary<TDictionaryKey, TDictionaryValue>>().ToDictionaryDictionary<TDictionaryKey, TDictionaryValue>();
		}
		
		public Dictionary<TOutputKey, Dictionary<TDictionaryKey, TDictionaryValue>> ToValueKeyDictionaryDictionary<TOutputKey, TDictionaryKey, TDictionaryValue>()
		{
			return GetDictionaryConverter<TOutputKey, Dictionary<TDictionaryKey, TDictionaryValue>>().ToValueKeyDictionaryDictionary<TDictionaryKey, TDictionaryValue>();
		}
		
	}
}
